/*
 * DERECHOS DE AUTOR (C) 2014 POR Jose Carlos Palma.
 *
 * TODOS LOS DERECHOS RESERVADOS. NINGUNA PARTE  DE ESTE CÓDIGO FUENTE 
 * PUEDE SER REPRODUCIDA O TRANSMITIDA O UTILIZADA EN  CUALQUIER FORMA 
 * O POR CUALQUIER  MEDIO, YA SEA  ELECTRÓNICO O MECÁNICO,  INCLUYENDO 
 * EL FOTOCOPIADO, GRABACIÓN O POR CUALQUIER SISTEMA DE ALMACENAMIENTO 
 * O RECUPERACIÓN DE INFORMACIÓN SIN  EL PERMISO PREVIO Y  POR ESCRITO
 * DEL PROPIETARIO DE LOS DERECHOS DE AUTOR.
 */
package org.dwh.pivot;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.MessageFormat;
import java.util.logging.Level;

/**
 * Clase que encapsula la lógica de la conexión con la base de datos.
 *
 * @author Jose Carlos Palma
 * @version v1.0
 */
public class Conexion implements Closeable {

    /**
     * Objeto conexión.
     */
    private Connection cxn = null;
    /**
     * Nombre de la conexión.
     */
    private String name;
    /**
     * SID de la Base de Datos.
     */
    private String sid;
    /**
     * Dirección IP del servidor.
     */
    private String ip;
    /**
     * Puerto de conexión de la Base de Datos.
     */
    private String port;
    /**
     * Usuario y/o Esquema de la Base de Datos.
     */
    private String user;
    /**
     * Contraseña correspondiente al usuario.
     */
    private String pass;
    /**
     * Cadena de Conexión de la Base de Datos (Oracle)
     */
    private StringBuilder cxnString;

    /**
     * Crea una instancia de Conexión, carga todos los atributos de la conexión
     * del archivo de configuración (sid, ip, user, etc).
     *
     * @param name es el nombre de la conexión.
     */
    public Conexion(String name) {
        try {
            Class.forName("oracle.jdbc.driver.OracleDriver");
        } catch (ClassNotFoundException ex) {
            throw new PivotatorlException("No se pudo cargar el driver de oracle.", ex, IExitCode.ERROR_LOAD_ORACLE_JDBC);
        }

        this.name = name;
        this.sid = System.getProperty(name + ".sid");
        this.ip = System.getProperty(name + ".ip");
        this.port = System.getProperty(name + ".port");
        this.user = System.getProperty(name + ".user");
        this.pass = System.getProperty(name + ".pass");

        this.cxnString = new StringBuilder("jdbc:oracle:thin:@").append(ip);
        cxnString.append(":").append(port).append(":").append(sid);

        Pivotator.log(Level.INFO, "Cadena de Conexion [{0}]", cxnString);

        createConnection();

    }

    /**
     * Crea la conexión de la base de datos.
     */
    private void createConnection() {
        if (cxn != null) {
            try {
                cxn.close();
                cxn = null;
            } catch (SQLException ex) {
            } finally {
                cxn = null;
            }
        }

        //Crea la conexion
        try {
            cxn = DriverManager.getConnection(cxnString.toString(), user, pass);
        } catch (SQLException ex) {
            throw new PivotatorlException("No se pudo crear la conexion de oracle.", ex, IExitCode.CANNOT_CREATE_CONNECTION_TO_DATABASE);
        }
    }

    /**
     * Indica si la conexión es valida o no.
     *
     * @return true si es valida sino retorna false.
     */
    public boolean isValid() {
        if (cxn == null) {
            return false;
        }
        boolean valid = false;
        try {
            valid = cxn.isValid(3);
        } catch (SQLException ex) {
        }
        return valid;
    }

    /**
     * Indica si la conexión está cerrada o no.
     *
     * @return true si la conexión está cerrada sino false.
     */
    public boolean isClose() {
        if (cxn == null) {
            return true;
        }
        boolean closed = true;
        try {
            closed = cxn.isClosed();
        } catch (SQLException ex) {
        }
        return closed;
    }

    /**
     * Retorna la conexión activa.
     *
     * @return un objeto Connection
     */
    public Connection getConnection() {
        if (!isValid()) {
            createConnection();
        } else if (isClose()) {
            createConnection();
        }
        return cxn;
    }

    /**
     * Obtiene la secuencia del log de la base de datos.
     *
     * @return un long que representa una secuencia única en la base de datos.
     */
    public long getLogSecuencia() {
        long retVal = -1;

        Statement stmt = null;
        try {
            getConnection();
            stmt = cxn.createStatement();
            ResultSet rs = stmt.executeQuery(System.getProperty("cubo.seqlog"));
            if (rs.next()) {
                retVal = rs.getInt("SEQ");
                rs.close();
            }
        } catch (Exception ex) {
            if (stmt != null) {
                try {
                    stmt.close();
                } catch (SQLException ex1) {
                }
            }
            throw new PivotatorlException("No se pudo obtener la secuencia del log", ex, IExitCode.CANNOT_GET_LOG_SEQ);
        }
        return retVal;
    }

    /**
     * Retorna una instancia de Cubo.
     *
     * @param id es el PK_CUBO del cubo.
     * @return un Cubo.
     */
    public Cubo getCubo(int id) {
        Cubo retVal = null;
        Statement stmt;
        ResultSet rs;
        String sql = MessageFormat.format(System.getProperty("cubo.get"), String.valueOf(id));
        try {
            Pivotator.log(Level.INFO, "Obteniendo la informacion del cubo en la DB");
            getConnection();
            stmt = cxn.createStatement();
            rs = stmt.executeQuery(sql);
            if (rs.next()) {
                retVal = new Cubo(rs.getInt("ID"),
                        rs.getString("NM"),
                        rs.getString("PATH"),
                        rs.getString("CGY"),
                        rs.getString("ACTV"),
                        rs.getString("ZIP"));
            } else {
                throw new Exception();
            }
        } catch (SQLException ex) {
            throw new PivotatorlException("No se pudo obtener la informacion del cubo.", ex, IExitCode.ERROR_EXECUTING_GET_CUBE_QUERY);
        } catch (Exception ex) {
            throw new PivotatorlException("No se pudo obtener la informacion del cubo.", ex, IExitCode.CANNOT_GET_CUBE_INFORMATION);
        }

        return retVal;
    }
    
    /**
     * Escribe al log inicio.
     * @param seq es la secuencia del log.
     * @param idCubo es el id del cubo.
     * @param pid es el pid de la aplicación del Pivotator.
     * @param pidExcel es el pid de la aplicación de Excel.
     */
    public void logInicio(long seq, int idCubo, int pid, int pidExcel) {
        Statement stmt;
        ResultSet rs;
        String sql = MessageFormat.format(System.getProperty("cubo.loginicio"), String.valueOf(seq), String.valueOf(idCubo), String.valueOf(pid), String.valueOf(pidExcel));

        try {
            getConnection();
            stmt = cxn.createStatement();
            stmt.executeUpdate(sql);
        } catch (SQLException ex) {
            throw new PivotatorlException("No se pudo escribir al log inicio.", ex, IExitCode.CANNOT_INSERTING_INITIAL_LOG);
        }

    }
    
    /**
     * Escribe al log fin.
     * @param seq es la secuencia del log.
     * @param correcto indica si es correcto "S" o no "N".
     * @param comment un comentario.
     * @param size es el tamaño del archivo.
     */
    public void logFin(long seq, String correcto, String comment, long size) {
        Statement stmt;
        ResultSet rs;
        String sql = MessageFormat.format(System.getProperty("cubo.logfin"), correcto, comment.replaceAll("[']", "''"), String.valueOf(seq), String.valueOf(size));

        try {
            getConnection();
            stmt = cxn.createStatement();
            stmt.executeUpdate(sql);
        } catch (SQLException ex) {
            throw new PivotatorlException("No se pudo escribir al log fin.", ex, IExitCode.CANNOT_INSERTING_FINAL_LOG);
        }

    }

    @Override
    public void close() {
        Pivotator.log(Level.INFO, "Cerrando la conexion a Oracle...");
        if (cxn != null) {
            try {
                cxn.close();
                cxn = null;
            } catch (SQLException ex) {
            } finally {
                cxn = null;
            }
        }
    }
}
